package com.zkh.myutils.scheduled.cron;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.List;
import java.util.function.Consumer;

import com.zkh.myutils.io.log.Logger;
import com.zkh.myutils.utils.StringUtils;

public class CronBean {
	//时间格式
	private static SimpleDateFormat datetimeFormat = new SimpleDateFormat("yyyyMMddHHmmss");
	//月份初始化
	private int month = 0;
	//cron表达式每部分
	private String[] cronParts = new String[7];
	//月份字符
	static String[] monthStr = {"","JAN","FEB","MAR","APR","MAY","JUN","JUL","AUG","SEP","OCT","NOV","DEC"};
	//星期字符
	static String[] weekStr= {"", "SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"};
	//每月刷新操作
	private Consumer<CronBean> reCalculator = e->{};
	
	//秒0~59
	private List<Integer> seconds = new ArrayList<>();
	//分0~59
	private List<Integer> minutes = new ArrayList<>();
	//时0~23
	private List<Integer> hours = new ArrayList<>();
	//日期1~31（周将解析到日期字段）
	private List<Integer> dates = new ArrayList<>();
	//月份1-12
	private List<Integer> months = new ArrayList<>();
	//年1970~2099
	private List<Integer> years = new ArrayList<>();
	
	/**
	 * 构造方法
	 * @param cronExpression 表达式，格式为：秒 分 小时 日期 月份 星期 年(可选)
	 */
	public CronBean(String cronExpression) {
		//为空
		if(StringUtils.isEmpty(cronExpression)) {
			throw new CronParseException("cron表达式不能为空");
		}
		//分割
		String[] parts = cronExpression.split(" ");
		//长度
		int len = parts.length;
		//判断格式
		if(len!=6 && len!=7) {
			throw new CronParseException("cron表达式格式错误");
		}
		//保存每个部分的值
		cronParts[0]=parts[0];cronParts[1]=parts[1];cronParts[2]=parts[2];
		cronParts[3]=parts[3];cronParts[4]=parts[4];cronParts[5]=parts[5];
		cronParts[6]=len==7?parts[6]:"";
		//替换月份（4）和星期（5）的英文缩写
		for(int i=1; i<=12; i++)
			if(cronParts[4].contains(monthStr[i]))
				cronParts[4] = cronParts[4].replaceAll(monthStr[i], String.valueOf(i));
		for(int i=1; i<=7; i++)
			if(cronParts[5].contains(weekStr[i]))
				cronParts[5] = cronParts[5].replaceAll(weekStr[i], String.valueOf(i));
		//判断格式
		if(StringUtils.isExistEmpty(cronParts[0], cronParts[1], cronParts[2], cronParts[3], cronParts[4], cronParts[5])) {
			throw new CronParseException("cron表达式格式错误");
		}
		//开始解析，返回当前月份
		int month = CronParser.parse(this);
		//处理解析月份
		if(this.month==0 || this.month!=month) {
			this.month = month;
		}
		//如果有特殊字符，先解析一遍
		reCalculator.accept(this);
	}
	
	/**
	 * 获取当前时间对应cron表达式的各部分数据。
	 * @return 返回长度为7的数组，分别对应cron表达式的7个部分
	 */
	public static int[] getNowTimeParts() {
		return getTimeParts(null);
	}
	
	/**
	 * 获取指定时间对应cron表达式的各部分数据。
	 * @param timestamp 时间戳，毫秒。不指定则获取当前时间
	 * @return 返回长度为7的数组，分别对应cron表达式的7个部分
	 */
	public static int[] getTimeParts(Long timestamp) {
		//当前时间
		Calendar calendar = Calendar.getInstance();
		//指定时间
		if(timestamp!=null) {
			calendar.setTimeInMillis(timestamp);
		}
		//秒 分 小时 日期 月份 星期 年
		return new int[] {calendar.get(Calendar.SECOND), calendar.get(Calendar.MINUTE), calendar.get(Calendar.HOUR_OF_DAY),
				calendar.get(Calendar.DATE), calendar.get(Calendar.MONTH)+1, calendar.get(Calendar.DAY_OF_WEEK),
				calendar.get(Calendar.YEAR)};
	}
	
	/**
	 * 获取格式化的时间点
	 */
	public static String getFormatTime(int[] timeParts) {
		//获取Calendar对象
		Calendar calendar = Calendar.getInstance();
		//设置值
		calendar.set(Calendar.YEAR, timeParts[6]);
		calendar.set(Calendar.MONTH, timeParts[4]-1);
		calendar.set(Calendar.DATE, timeParts[3]);
		calendar.set(Calendar.HOUR_OF_DAY, timeParts[2]);
		calendar.set(Calendar.MINUTE, timeParts[1]);
		calendar.set(Calendar.SECOND, timeParts[0]);
		//返回
		return datetimeFormat.format(calendar.getTime());
	}
	
	/**
	 * 检查cron表达式是否匹配当前时间<br>
	 * <b>如果同时创建多个cron表达式，且需要依次等待执行，那么不建议使用该方法，可以统一通过getNowTimeParts()方法获取时间，再调用isMatched(int[] nowTimeParts)方法</b>
	 */
	public boolean isMatched() {
		//获取时间
		int[] n = getNowTimeParts();
		//显示日志
		Logger.debug("cron时间：" + Arrays.toString(n));
		//匹配
		return isMatched(n);
	}
	
	/**
	 * 检查cron表达式是否匹配指定时间
	 * @param nowTimeParts 当前时间对应cron表达式的各部分数据
	 */
	public boolean isMatched(int[] nowTimeParts) {
		//取别名，方便取
		int[] n = nowTimeParts;
		//检查是否跨月份，如果跨月份，则重新解析日期
		if(month!=n[4]) {
			//解析日期
			reCalculator.accept(this);
			//保存月份
			month = n[4];
		}
		//开始作匹配
		return seconds.contains(n[0]) && minutes.contains(n[1]) && hours.contains(n[2]) && dates.contains(n[3])
				&& months.contains(n[4]) && years.contains(n[6]);
	}

	public List<Integer> getSeconds() {
		return seconds;
	}
	public void setSeconds(List<Integer> seconds) {
		this.seconds = seconds;
	}
	public List<Integer> getMinutes() {
		return minutes;
	}
	public void setMinutes(List<Integer> minutes) {
		this.minutes = minutes;
	}
	public List<Integer> getHours() {
		return hours;
	}
	public void setHours(List<Integer> hours) {
		this.hours = hours;
	}
	public List<Integer> getDates() {
		return dates;
	}
	public void setDates(List<Integer> dates) {
		this.dates = dates;
	}
	public List<Integer> getMonths() {
		return months;
	}
	public void setMonths(List<Integer> months) {
		this.months = months;
	}
	public List<Integer> getYears() {
		return years;
	}
	public void setYears(List<Integer> years) {
		this.years = years;
	}
	
	@Override
	public String toString() {
		return "CronBean [\r\n"
				+ "\tcronParts=" + Arrays.toString(cronParts) + "\r\n"
				+ "\tseconds=" + seconds + "\r\n"
				+ "\tminutes=" + minutes + "\r\n"
				+ "\thours=" + hours + "\r\n"
				+ "\tdates=" + dates + "\r\n"
				+ "\tmonths=" + months + "\r\n"
				+ "\tyears=" + years + "\r\n"
				+ "\tmonth="+month+"\r\n"
				+ "]";
	}

	/*内部使用的方法**/
	String[] getCronParts() {
		return cronParts;
	}
	Consumer<CronBean> getReCalculator() {
		return reCalculator;
	}
	void setReCalculator(Consumer<CronBean> reCalculator) {
		this.reCalculator = reCalculator;
	}
}
